#pragma once

#include <deque>
#include <memory>
#include "NetUtil.h"
#include "WorkerPool.h"

namespace wsbus {

	class ConnectorImpl;
	typedef std::shared_ptr<beast::flat_buffer> BufferPtr;
	typedef std::shared_ptr<ConnectorImpl> ConnectorImplPtr;

	class ConnectorImpl : public std::enable_shared_from_this<ConnectorImpl>
	{
	public:
		struct HandleCallback {
			std::function<void(bool success)> handleConnected;
			std::function<void(const BufferPtr&)> handleRead;
			std::function<void(int errorCode)> handleError;
		};

#ifdef WS_SSL
		ConnectorImpl(net::io_context &ioc, ssl::context &ctx, const std::string &host, const std::string &port);
#else
		ConnectorImpl(net::io_context &ioc, const std::string &host, const std::string &port);
#endif
		~ConnectorImpl();
		void run();

		const std::string& host() const { return host_; }
		const std::string& port() const { return port_; }
		HandleCallback getHandleCallback() const { return handleCallback_; }
		void setHandleCallback(const HandleCallback &cb) { handleCallback_ = cb; }
		void post(const BufferPtr &msg);
		time_t lastWriteMessageTime() const { return writeMessageTime_; }

	private:
		void onResolve(beast::error_code ec, tcp::resolver::results_type results);
		void onConnect(beast::error_code ec, tcp::resolver::results_type::endpoint_type ep);
#ifdef WS_SSL
		void onSSLHandshake(beast::error_code ec);
#endif
		void onHandshake(beast::error_code ec);
		void doClose();
		void onConnectError(beast::error_code ec, char const* what);
		void onError(beast::error_code ec, char const* what);
		void doWrite(const BufferPtr &msg);
		void doWrite();
		void onWrite(beast::error_code ec, std::size_t bytes_transferred);
		void doRead();
		void onRead(beast::error_code ec, std::size_t bytes_transferred);
		void handleRead(const BufferPtr &msg);

	private:
		std::string host_;
		std::string port_;
		std::string address_;
		net::io_context &ioc_;
		tcp::resolver resolver_;
#ifdef WS_SSL
		websocket::stream<beast::ssl_stream<beast::tcp_stream>> ws_;
#else
		websocket::stream<beast::tcp_stream> ws_;
#endif
		WorkerPool readWorker_;
		BufferPtr readBuffer_;
		std::deque<BufferPtr> writeMessages_;
		time_t writeMessageTime_;
		HandleCallback handleCallback_;
	};


}